using System.Configuration;
using System.Diagnostics;
using System.IO.Ports;
using System.Resources;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization;
using static System.Net.Mime.MediaTypeNames;
using static System.Windows.Forms.AxHost;

// 添加Excel相关引用，EPPlus版本要求7.x，否则无法用后续方法设置非商业许可
using OfficeOpenXml;
using OfficeOpenXml.Style;
using System.Drawing;

namespace RoboticArmTestHost
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort = new SerialPort();
        private byte ID = 0;
        private bool isTesting = false;
        private CancellationTokenSource? cancelTest;
        private const int DefaultReadTimeout = 1000;
        private uint version = 0;
        private int idel_min = 20;
        private int idel_max = 30;
        private int current_max = 0;
        private int current_min = 0;
        private int speed = 0;
        private int pos = 0;
        private byte pos_cmd = 0;
        private int speed_error = 5;
        private int pos_error = 5;
        private int voltage_min = 12;
        private int voltage_max = 16;
        private int temp_min = 0;
        private int temp_max = 75;
        private int pos_time = 3000;
        private int open_time = 75;
        private int open_speed = 1000;

        private string uuid = "";
        private string scannedBarcode = ""; // 新增：存储扫描的条码

        private Dictionary<string, DeviceConfig> deviceConfigs;

        // 添加测试结果数据结构
        public class TestResult
        {
            public DateTime TestTime { get; set; }
            public string? DeviceModel { get; set; }
            public string? uuid { get; set; }
            public string? ScannedBarcode { get; set; } // 新增：扫描的条码
            public byte DeviceID { get; set; }
            public string? SerialPort { get; set; }
            public string? FirmwareVersion { get; set; }
            public string? FirmwareResult { get; set; }
            public string? IdleCurrent { get; set; }
            public string? IdleResult { get; set; }
            public string? LoadCurrent { get; set; }
            public string? LoadResult { get; set; }
            public string? Speed { get; set; }
            public string? SpeedResult { get; set; }
            public string? Position { get; set; }
            public string? PositionResult { get; set; }
            public string? Temperature { get; set; }
            public string? TempResult { get; set; }
            public string? Voltage { get; set; }
            public string? VoltageResult { get; set; }
            public string? OpenLoopTest { get; set; }
            public string? OverallResult { get; set; }
        }

        private TestResult currentTestResult;

        public class DeviceConfig
        {
            public int current_max { get; set; }
            public int current_min { get; set; }
            public uint version { get; set; }
            public int speed { get; set; }
            public int pos { get; set; }
            public byte pos_cmd { get; set; }
            public int pos_error { get; set; }
            public int idel_max { get; set; }
            public int idel_min { get; set; }
            public int voltage_min { get; set; }
            public int voltage_max { get; set; }
            public int temp_min { get; set; }
            public int temp_max { get; set; }
            public int speed_error { get; set; }
            public int pos_time { get; set; }
            public int open_time { get; set; }
            public int open_speed { get; set; }
        }

        public class AppConfig : Dictionary<string, DeviceConfig> { }

        public Form1()
        {
            InitializeComponent();
            LoadConfigurations();
            ExcelPackage.LicenseContext = LicenseContext.NonCommercial;  // 设置EPPlus许可（非商业用途）
        }

        private void TextBox1_GotFocus(object? sender, EventArgs e)
        {
            if (textBox1.Text == "请扫描产品条码...")
            {
                textBox1.Text = "";
                textBox1.ForeColor = Color.Black;   
            }
        }

        private void TextBox1_LostFocus(object? sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(textBox1.Text))
            {
                textBox1.Text = "请扫描产品条码...";
                textBox1.ForeColor = Color.Gray;
            }
        }

        private void TextBox1_KeyPress(object? sender, KeyPressEventArgs e)
        {
            // 当按下回车键时，验证条码
            if (e.KeyChar == (char)Keys.Enter)
            {
                e.Handled = true;
                ValidateAndProcessBarcode();
            }
        }

        private void TextBox1_TextChanged(object? sender, EventArgs e)
        {
            // 实时验证条码长度
            if (textBox1.Text.Length == 27 && textBox1.Text != "请扫描产品条码...")
            {
                ValidateAndProcessBarcode();
            }
            else
            {
                textBox1.BackColor = Color.White;
                btnStartTest.Enabled = false;
            }
        }

        private void ValidateAndProcessBarcode()
        {
            if (isExamineMode)  // 如果是检查模式，跳过条码验证
            {
                btnStartTest.Enabled = serialPort.IsOpen;
                return;
            }
            string barcode = textBox1.Text.Trim();
            if (string.IsNullOrEmpty(barcode) || barcode == "请扫描产品条码...")
            {
                MessageBox.Show("请先扫描产品条码！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                textBox1.Focus();
                return;
            }
            var validationResult = ValidateDreameBarcode(barcode);
            if (validationResult.IsValid)
            {
                scannedBarcode = barcode;
                textBox1.BackColor = Color.LightGreen;
                btnStartTest.Enabled = true;
                btnStartTest.Enabled = btnStartTest.Enabled && serialPort.IsOpen;
            }
            else
            {
                scannedBarcode = "";
                textBox1.BackColor = Color.LightCoral;
                MessageBox.Show($"条码验证失败：{validationResult.ErrorMessage}", "条码错误",
                               MessageBoxButtons.OK, MessageBoxIcon.Error);

                textBox1.Text = "";
                textBox1.Focus();
            }
        }

        // 简化的条码验证方法 - 只验证27位长度
        private BarcodeValidationResult ValidateDreameBarcode(string barcode)
        {
            var result = new BarcodeValidationResult();
            // 只验证长度是否为27位
            if (barcode.Length != 27)
            {
                result.ErrorMessage = $"条码长度错误，应为27位，实际为{barcode.Length}位";
                return result;
            }
            result.IsValid = true;
            return result;
        }

        // 简化的条码验证结果类
        public class BarcodeValidationResult
        {
            public bool IsValid { get; set; } = false;
            public string ErrorMessage { get; set; } = "";
        }

        private void LoadConfigurations()
        {
            const string configPath = "servo_config.yaml";
            try
            {
                if (!File.Exists(configPath))
                {
                    throw new FileNotFoundException("配置文件不存在", configPath);
                }

                var deserializer = new DeserializerBuilder()
                    .WithNamingConvention(UnderscoredNamingConvention.Instance)
                    .IgnoreUnmatchedProperties()
                    .Build();
                deviceConfigs = deserializer.Deserialize<AppConfig>(
                    File.ReadAllText(configPath));
                // 验证基础配置
                if (deviceConfigs?.Count < 6)
                {
                    throw new InvalidDataException("基础配置缺失，至少需要id1-id6配置项");
                }
            }
            catch (Exception ex)
            {
                // 记录详细错误日志
                File.AppendAllText("error.log", $"[{DateTime.Now}] 配置加载失败：{ex}\n");
                MessageBox.Show($"已加载默认配置，原因：{ex.Message}");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            RefreshPorts();
            // 启动时自动聚焦到textBox1
            this.ActiveControl = textBox1;
            // 设置现有Excel文件为只读模式
            SetExcelFilesReadOnly();
            textBox1.Focus();
        }

        private void cmbID_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                var id = cmbID.SelectedIndex + 1;
                ID = (byte)id;
                if (id < 1 || id > 6)
                {
                    throw new ArgumentOutOfRangeException("ID范围应为1-6");
                }

                var config = GetConfiguration(id);
                ApplyConfiguration(config);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"配置加载失败：{ex.Message}");
                // 恢复安全状态
                cmbID.SelectedIndex = -1;
            }
        }

        private DeviceConfig GetConfiguration(int id)
        {
            var configKey = $"id{id}";

            if (deviceConfigs == null)
            {
                throw new InvalidOperationException("配置未初始化");
            }
            if (!deviceConfigs.TryGetValue(configKey, out var config))
            {
                throw new KeyNotFoundException($"缺少 {configKey} 的配置定义");
            }
            return config;
        }

        private void ApplyConfiguration(DeviceConfig config)
        {
            current_max = config.current_max;
            current_min = config.current_min;
            version = config.version;
            speed = config.speed;
            pos = config.pos;
            pos_cmd = config.pos_cmd;
            pos_error = config.pos_error;
            idel_max = config.idel_max;
            idel_min = config.idel_min;
            voltage_min = config.voltage_min;
            voltage_max = config.voltage_max;
            temp_min = config.temp_min;
            temp_max = config.temp_max;
            pos_time = config.pos_time;
            open_time = config.open_time;
            open_speed = config.open_speed;
            speed_error = config.speed_error;
        }

        private void RefreshPorts()
        {
            string? currentPort = cmbPort.SelectedItem?.ToString();

            cmbPort.BeginUpdate();
            cmbPort.Items.Clear();

            var ports = SerialPort.GetPortNames().OrderBy(p => p);
            cmbPort.Items.AddRange(ports.ToArray());

            if (!string.IsNullOrEmpty(currentPort) && ports.Contains(currentPort))
                cmbPort.SelectedItem = currentPort;
            else if (cmbPort.Items.Count > 0)
                cmbPort.SelectedIndex = 0;

            cmbPort.EndUpdate();
        }

        private void btnOpenClose_Click(object sender, EventArgs e)
        {
            if (serialPort.IsOpen)
            {
                CloseSerialPort();
            }
            else
            {
                OpenSerialPort();
            }
        }

        private void OpenSerialPort()
        {
            textBox1.Focus();
            if (cmbPort.SelectedItem == null)
            {
                MessageBox.Show("请选择有效的串口！");
                return;
            }
            
            try
            {
                serialPort.PortName = cmbPort.SelectedItem.ToString();
                serialPort.BaudRate = 115200;
                serialPort.Parity = Parity.None;
                serialPort.DataBits = 8;
                serialPort.StopBits = StopBits.One;
                serialPort.ReadTimeout = DefaultReadTimeout;
                serialPort.Open();
                UpdateControls(true);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"打开串口失败: {ex.Message}");
            }
        }

        private void CloseSerialPort()
        {
            try
            {
                serialPort.Close();
                UpdateControls(false);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"关闭串口失败: {ex.Message}");
            }
        }

        private void UpdateControls(bool isOpen)
        {
            btnOpenClose.Text = isOpen ? "关闭" : "打开";
            cmbPort.Enabled = !isOpen;
            // 只有在串口打开且条码验证通过时才能开始测试
            btnStartTest.Enabled = isOpen && !string.IsNullOrEmpty(scannedBarcode);
        }

        private void cmbPort_DropDown(object sender, EventArgs e)
        {
            RefreshPorts();
        }

        private void btnStartTest_Click(object sender, EventArgs e)
        {
            if(!isExamineMode)
            {
                ValidateAndProcessBarcode();
                if (string.IsNullOrEmpty(scannedBarcode) || textBox1.Text.Length != 27)
                {
                    MessageBox.Show("请先扫描有效的产品条码！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    textBox1.Focus();
                    return;
                }
            }
            else
            {
                if (string.IsNullOrEmpty(scannedBarcode))  // 检查模式下确保有扫描条码值用于记录
                {
                    scannedBarcode = "EXAMINE_MODE_" + DateTime.Now.ToString("yyyyMMddHHmmss");
                }
            }
            if (isTesting)
            {
                cancelTest?.Cancel();
                return;
            }
            isTesting = true;
            cancelTest = new CancellationTokenSource();
            btnStartTest.Text = "停止";
            // 初始化测试结果
            currentTestResult = new TestResult
            {
                TestTime = DateTime.Now,
                DeviceModel = cmbID.SelectedItem?.ToString() ?? "未知",
                DeviceID = ID,
                SerialPort = cmbPort.SelectedItem?.ToString() ?? "未知",
                ScannedBarcode = scannedBarcode // 保存扫描的条码
            };
            try
            {
                for (int i = 1; i <= 7; i++)
                {
                    var foundControls1 = this.Controls.Find($"value{i}", true);
                    var foundControls2 = this.Controls.Find($"result{i}", true);
                    var foundControls3 = this.Controls.Find($"panel{i}", true);
                    foundControls1[0].Text = "";
                    foundControls2[0].Text = "";
                    foundControls3[0].BackColor = Color.WhiteSmoke;
                }
                value8.Text = "";
                panel8.BackColor = Color.WhiteSmoke;
                state = TestState.ReadVersion;
                Task.Run(() => RunTestSequence(cancelTest.Token), cancelTest.Token);
            }
            catch (OperationCanceledException)
            {
                isTesting = false;
                cancelTest?.Cancel();
                btnStartTest.Text = "测试";
            }
        }

        private enum TestState
        {
            ReadVersion = 1,
            Idle,
            ReadCurrent,
            ReadSpeed,
            ReadPos,
            ReadTemperature,
            ReadVoltage,
            open,
            Finished
        }
        public static int AveragePro(int[] inputData)
        {
            try
            {
                if (inputData == null || inputData.Length == 0)
                    return -1;
                var data = inputData.Select(x => (double)x).ToArray();
                double mean = data.Average();
                double stdDev = Math.Sqrt(data.Sum(x => Math.Pow(x - mean, 2)) / data.Length);
                double lowerBound = mean - 3 * stdDev;
                double upperBound = mean + 3 * stdDev;
                var filtered = data.Where(x => x >= lowerBound && x <= upperBound).ToArray();
                if (filtered.Length == 0)
                    return -1;
                return (int)Math.Round(filtered.Average());
            }
            catch
            {
                return -1;
            }
        }

        TestState state = TestState.ReadVersion;

        private void RunTestSequence(CancellationToken token)
        {
            bool hasFailure = false;  // 总体测试结果
            while (!token.IsCancellationRequested && state != TestState.Finished)
            {
                try
                {
                    var foundControls = this.Controls.Find($"value{(int)state}", true);
                    int response_value = -2147483647;
                    if (state < TestState.open)
                    {
                        foundControls[0].Invoke((MethodInvoker)(() =>
                        {
                            foundControls[0].Text = "测试中";
                        }));
                    }
                    else
                    {
                        foundControls[0].Invoke((MethodInvoker)(() =>
                        {
                            foundControls[0].Text = "测试中,请自行听音辨认";
                        }));
                    }
                    byte[] off_mo = [0x00];
                    SendCommand(0x03, 0x92, off_mo);

                    switch (state)
                    {
                        case TestState.ReadVersion:
                            response_value = (int)GetResponseParameters(SendCommand(0x02, 0x0c));
                            uuid = GetUid(SendCommand(0x02, 0xf0));
                            if (response_value == -2147483647)
                            {
                                Invoke(() => value1.Text = "无法获取");
                                Invoke(() => result1.Text = "NG");
                                Invoke(() => panel1.BackColor = Color.Red);
                                currentTestResult.FirmwareVersion = "无法获取";
                                currentTestResult.FirmwareResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            Invoke(() => value1.Text = "0X" + response_value.ToString("X8"));
                            currentTestResult.FirmwareVersion = "0X" + response_value.ToString("X8");
                            if (response_value != version)
                            {
                                Invoke(() => result1.Text = "NG");
                                Invoke(() => panel1.BackColor = Color.Red);
                                currentTestResult.FirmwareResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result1.Text = "OK");
                                Invoke(() => panel1.BackColor = Color.Green);
                                currentTestResult.FirmwareResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.Idle:
                            short[] idle_test = new short[20];
                            for (int i = 0; i < 20; i++)
                            {
                                Thread.Sleep(10);
                                idle_test[i] = (short)GetResponseParameters(SendCommand(0x02, 0x48));
                                if (idle_test[i] == -2147483647)
                                {
                                    break;
                                }
                            }
                            response_value = AveragePro(idle_test.Select(x => (int)x).ToArray());
                            if (response_value == -2147483647)
                            {
                                Invoke(() => value2.Text = "无法获取");
                                Invoke(() => result2.Text = "NG");
                                Invoke(() => panel2.BackColor = Color.Red);
                                currentTestResult.IdleCurrent = "无法获取";
                                currentTestResult.IdleResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            Invoke(() => value2.Text = response_value.ToString() + "mA");
                            currentTestResult.IdleCurrent = response_value.ToString();
                            if (response_value < idel_min || response_value > idel_max)
                            {
                                Invoke(() => result2.Text = "NG");
                                Invoke(() => panel2.BackColor = Color.Red);
                                currentTestResult.IdleResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result2.Text = "OK");
                                Invoke(() => panel2.BackColor = Color.Green);
                                currentTestResult.IdleResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.ReadCurrent:

                            byte[] speed_data = [(byte)(speed & 0xff), (byte)((speed >> 8) & 0xff)];
                            //SendCommand(0x03, 0x5D, speed_data); //0x5D：速度限制寄存器地址
                            SendCommand(0x03, 0x6F, speed_data);
                            Thread.Sleep(3500);
                            int[] current_test = new int[20];
                            int current_sum = 0;
                            for (int i = 0; i < 20; i++)
                            {
                                current_test[i] = (int)GetResponseParameters(SendCommand(0x02, 0x48));
                                if (current_test[i] == -2147483647)
                                {
                                    break;
                                }
                            }
                            response_value = AveragePro(current_test);
                            if (response_value == -2147483647)
                            {
                                Invoke(() => value3.Text = "无法获取");
                                Invoke(() => result3.Text = "NG");
                                Invoke(() => panel3.BackColor = Color.Red);
                                currentTestResult.LoadCurrent = "无法获取";
                                currentTestResult.LoadResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            Invoke(() => value3.Text = response_value.ToString() + "mA");
                            currentTestResult.LoadCurrent = response_value.ToString();
                            if (response_value < current_min || response_value > current_max)
                            {
                                Invoke(() => result3.Text = "NG");
                                Invoke(() => panel3.BackColor = Color.Red);
                                currentTestResult.LoadResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result3.Text = "OK");
                                Invoke(() => panel3.BackColor = Color.Green);
                                currentTestResult.LoadResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.ReadSpeed:
                            int[] speed_test = new int[20];
                            float speed_max = (float)speed + (float)speed * (float)speed_error / 100.0f;
                            float speed_min = (float)speed - (float)speed * (float)speed_error / 100.0f;
                            for (int i = 0; i < 20; i++)
                            {
                                speed_test[i] = (int)GetResponseParameters(SendCommand(0x02, 0x47));
                                if (speed_test[i] == -2147483647)
                                {
                                    break;
                                }
                            }
                            response_value = AveragePro(speed_test);
                            if (response_value == -2147483647)
                            {
                                Invoke(() => value4.Text = "无法获取");
                                Invoke(() => result4.Text = "NG");
                                Invoke(() => panel4.BackColor = Color.Red);
                                currentTestResult.Speed = "无法获取";
                                currentTestResult.SpeedResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            SendCommand(0x03, 0x6F, [0x00, 0x00]);
                            Thread.Sleep(100);
                            SendCommand(0x03, pos_cmd, [0x00, 0x00]);
                            var stopwatch = Stopwatch.StartNew();
                            while (true)
                            {
                                int pos_now = (int)GetResponseParameters(SendCommand(0x02, 0x46));
                                if (Math.Abs(pos_now) < 10 || stopwatch.ElapsedMilliseconds > 5000)
                                {
                                    break;
                                }
                            }
                            Invoke(() => value4.Text = response_value.ToString() + "°/s");
                            currentTestResult.Speed = response_value.ToString();
                            if (response_value < speed_min || response_value > speed_max)
                            {
                                Invoke(() => result4.Text = "NG");
                                Invoke(() => panel4.BackColor = Color.Red);
                                currentTestResult.SpeedResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result4.Text = "OK");
                                Invoke(() => panel4.BackColor = Color.Green);
                                currentTestResult.SpeedResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.ReadPos:
                            int[] pos_test = [-pos, pos, 0];
                            byte[] pos_data = new byte[2];
                            bool pos_flag = true;
                            string positionValues = "";
                            Invoke(() => value5.Text = "");
                            int response_pos = -2147483647;
                            for (int i = 0; i < 3; i++)
                            {
                                pos_data = [(byte)(pos_test[i] & 0xff), (byte)((pos_test[i] >> 8) & 0xff)];
                                SendCommand(0x03, pos_cmd, pos_data);
                                var stopwatch3 = Stopwatch.StartNew();
                                while (true)
                                {
                                    int pos_now = (int)(GetResponseParameters(SendCommand(0x02, 0x46)) & 0XFFFFFFFF);
                                    if (Math.Abs(pos_now - pos_test[i]) < 10 || stopwatch3.ElapsedMilliseconds > pos_time * 1.5)
                                    {
                                        break;
                                    }
                                }
                                Thread.Sleep(pos_time / 10);
                                byte[] debugbyte = SendCommand(0x02, 0x46);

                                response_pos = (int)(GetResponseParameters(SendCommand(0x02, 0x46)) & 0XFFFFFFFF);

                                if (response_pos == -2147483647)
                                {
                                    break;
                                }
                                if (Math.Abs(response_pos - pos_test[i]) > pos_error)
                                {
                                    pos_flag = false;
                                }
                                Invoke(() => value5.Text += ((float)response_pos / 10.0).ToString("F1") + "°" + "  ");
                                //Invoke(() => value5.Text += response_value.ToString() + "°" + "  ");
                                //positionValues += response_pos.ToString() + "  ";
                                positionValues += ((float)response_pos / 10.0).ToString("F1")  + "  ";
                    }
                            if (response_pos == -2147483647)
                            {
                                Invoke(() => value5.Text = "无法获取");
                                Invoke(() => result5.Text = "NG");
                                Invoke(() => panel5.BackColor = Color.Red);
                                currentTestResult.Position = "无法获取";
                                currentTestResult.PositionResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            currentTestResult.Position = positionValues.Trim();
                            if (pos_flag == false)
                            {
                                Invoke(() => result5.Text = "NG");
                                Invoke(() => panel5.BackColor = Color.Red);
                                currentTestResult.PositionResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result5.Text = "OK");
                                Invoke(() => panel5.BackColor = Color.Green);
                                currentTestResult.PositionResult = "OK";
                            }
                            state = state + 1;
                            break;

                        case TestState.ReadTemperature:
                            int temp_test = 0;
                            int temp_sum = 0;
                            for (int i = 0; i < 5; i++)
                            {
                                temp_test = (int)GetResponseParameters(SendCommand(0x02, 0x4A));
                                if (temp_test == -2147483647)
                                {
                                    break;
                                }
                                temp_sum += temp_test;
                            }
                            if (temp_test == -2147483647)
                            {
                                Invoke(() => value6.Text = "无法获取");
                                Invoke(() => result6.Text = "NG");
                                Invoke(() => panel6.BackColor = Color.Red);
                                currentTestResult.Temperature = "无法获取";
                                currentTestResult.TempResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            response_value = temp_sum / 5;
                            Invoke(() => value6.Text = response_value.ToString() + "℃");
                            currentTestResult.Temperature = response_value.ToString();
                            if (response_value < temp_min || response_value > temp_max)
                            {
                                Invoke(() => result6.Text = "NG");
                                Invoke(() => panel6.BackColor = Color.Red);
                                currentTestResult.TempResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result6.Text = "OK");
                                Invoke(() => panel6.BackColor = Color.Green);
                                currentTestResult.TempResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.ReadVoltage:
                            int voltage_test = 0;
                            int voltage_sum = 0;
                            for (int i = 0; i < 5; i++)
                            {
                                voltage_test = (int)GetResponseParameters(SendCommand(0x02, 0x4B));
                                if (voltage_test == -2147483647)
                                {
                                    break;
                                }
                                voltage_sum += voltage_test;
                            }
                            if (voltage_test == -2147483647)
                            {
                                Invoke(() => value7.Text = "无法获取");
                                Invoke(() => result7.Text = "NG");
                                Invoke(() => panel7.BackColor = Color.Red);
                                currentTestResult.Voltage = "无法获取";
                                currentTestResult.VoltageResult = "NG";
                                hasFailure = true;
                                state = state + 1;
                                break;
                            }
                            response_value = voltage_sum / 5;
                            string voltageStr = ((float)response_value / 1000).ToString("F2");
                            Invoke(() => value7.Text = ((float)response_value / 1000).ToString("F2") + "V");
                            currentTestResult.Voltage = voltageStr;
                            if ((float)response_value / 1000 < voltage_min || (float)response_value / 1000 > voltage_max)
                            {
                                Invoke(() => result7.Text = "NG");
                                Invoke(() => panel7.BackColor = Color.Red);
                                currentTestResult.VoltageResult = "NG";
                                hasFailure = true;
                            }
                            else
                            {
                                Invoke(() => result7.Text = "OK");
                                Invoke(() => panel7.BackColor = Color.Green);
                                currentTestResult.VoltageResult = "OK";
                            }
                            state = state + 1;
                            break;
                        case TestState.open:
                            Invoke(() => timer1.Enabled = true);
                            byte[] open_speed_cw = [(byte)(open_speed & 0xff), (byte)((open_speed >> 8) & 0xff)];
                            byte[] open_speed_ccw = [(byte)(-open_speed & 0xff), (byte)((-open_speed >> 8) & 0xff)];
                            SendCommand(0x03, 0x6e, open_speed_cw);
                            Thread.Sleep(open_time);
                            SendCommand(0x03, 0x6e, open_speed_ccw);
                            Thread.Sleep(open_time);
                            SendCommand(0x03, 0x6e, [0x00, 0x00]);
                            Thread.Sleep(500);
                            SendCommand(0x03, pos_cmd, [0x00, 0x00]);
                            var stopwatch2 = Stopwatch.StartNew();
                            while (true)
                            {
                                int pos_now = (int)GetResponseParameters(SendCommand(0x02, 0x46));
                                if (Math.Abs(pos_now) < 10 || stopwatch2.ElapsedMilliseconds > 5000)
                                {
                                    break;
                                }
                            }
                            state = state + 1;
                            Invoke(() => timer1.Enabled = false);

                            Invoke(() => panel8.BackColor = Color.Orange);
                            Invoke(() => value8.Text = "测试完毕");
                            if (currentTestResult != null)
                            {
                                currentTestResult.OpenLoopTest = "已执行";
                                currentTestResult.OverallResult = hasFailure ? "NG" : "OK";  // 设置总体测试结果
                                SaveTestResultToExcel(currentTestResult); // 保存测试结果到Excel
                            }
                            break;
                        default:
                            state = TestState.ReadVersion;
                            break;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"测试失败：{ex.Message}\n\n{ex.StackTrace}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                }
            }

            isTesting = false;
            Invoke(() => btnStartTest.Text = "测试");

            // 测试完成后重新聚焦到textBox1并清空内容准备下次扫描
            Invoke(() =>
            {
                if (!isExamineMode)
                {
                    scannedBarcode = "";
                    textBox1.Text = "";
                    textBox1.BackColor = Color.White;
                    textBox1.Focus();
                }
                else
                {
                    textBox1.Text = "出货检查模式 - 无需扫码";
                    textBox1.BackColor = Color.LightGray;
                }
                // 重新更新按钮状态
                //btnStartTest.Enabled = serialPort.IsOpen && !string.IsNullOrEmpty(scannedBarcode);
                btnStartTest.Enabled = serialPort.IsOpen && (isExamineMode || !string.IsNullOrEmpty(scannedBarcode));
            });
        }
        
        private void SaveTestResultToExcel(TestResult result)
        {
            FileStream lockStream = null;
            bool wasReadOnly = false; // 记录文件原始只读状态
            //string folderPath1 = Path.Combine(System.Windows.Forms.Application.StartupPath, "数据");
            //string parentDirectory = Directory.GetParent(System.Windows.Forms.Application.StartupPath).FullName; // 获取应用程序启动路径的上一级目录
            //string folderPath = Path.Combine(parentDirectory, "测试数据备份"); // 拼接路径：上一级目录 + "测试数据备份"
            // 获取当前应用程序的启动路径
            string currentDirectory = System.Windows.Forms.Application.StartupPath;
            // 使用相对路径返回上一级目录
            string parentDirectory = Path.Combine(currentDirectory, "..");
            // 获取上一级目录的完整路径
            string fullParentDirectory = Path.GetFullPath(parentDirectory);
            // 拼接路径：上一级目录 + "测试数据备份"
            string folderPath = Path.Combine(fullParentDirectory, "测试数据备份");
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath);
            }
            // 获取舵机型号
            string servoModel = "";
            try
            {
                // 在UI线程中获取cmbID的值
                this.Invoke((MethodInvoker)(() =>
                {
                    if (cmbID.SelectedItem != null)
                    {
                        string selectedText = cmbID.SelectedItem.ToString();
                        int dashIndex = selectedText.LastIndexOf("- ");// 提取型号部分，例如从"底座舵机（ID1）- B11L"中提取"B11L"
                        if (dashIndex >= 0 && dashIndex + 2 < selectedText.Length)
                        {
                            servoModel = selectedText.Substring(dashIndex + 2).Trim();
                            servoModel = string.Join("", servoModel.Split(Path.GetInvalidFileNameChars()));// 清理文件名中的非法字符
                        }
                        else
                        {
                            servoModel = $"ID{ID}"; // 如果没有找到"- "，则使用ID号
                        }
                    }
                    else
                    {
                        servoModel = $"ID{ID}";
                    }
                }));
            }
            catch
            {
                servoModel = $"ID{ID}";// 如果获取失败，使用ID作为备选
            }
            string computerName = Environment.MachineName;
            string fileName = $"数据{DateTime.Now:yyyy-MM-dd}_{servoModel}_{computerName}.xlsx";
            string filePath = Path.Combine(folderPath, fileName);
            string lockFilePath = filePath + ".lock";
            try
            {
                if (!TryCreateLockFile(lockFilePath, out lockStream))
                {
                    Invoke(() =>
                    {
                        MessageBox.Show("文件正在被其他进程使用，请稍后重试。",
                            "文件占用", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    });
                    return;
                }
                // 检查并临时解除只读权限
                if (File.Exists(filePath))
                {
                    FileAttributes attributes = File.GetAttributes(filePath);
                    wasReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;

                    if (wasReadOnly)
                    {
                        // 临时解除只读权限
                        File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
                    }
                }

                FileInfo file = new FileInfo(filePath);
                using (ExcelPackage package = file.Exists ? new ExcelPackage(file) : new ExcelPackage())
                {
                    ExcelWorksheet worksheet;
                    if (package.Workbook.Worksheets.Count > 0)  // 如果文件存在且有工作表，使用第一个工作表；否则创建新工作表
                    {
                        worksheet = package.Workbook.Worksheets[0];
                        if (worksheet.Protection.IsProtected)
                        {
                            worksheet.Protection.SetPassword(""); // 新增：临时解除工作表保护以便添加数据
                        }
                    }
                    else
                    {
                        worksheet = package.Workbook.Worksheets.Add("测试结果");
                        string[] headers = {
                    "测试时间", "设备型号", "设备UUID", "SN码", "串口",
                    "固件版本", "固件结果", "静态电流(mA)", "静态结果",
                    "空载电流(mA)", "空载结果", "速度(°/s)", "速度结果",
                    "位置(°)", "位置结果", "温度(℃)", "温度结果",
                    "电压(V)", "电压结果", "开环测试", "总体结果"
                };
                        for (int i = 0; i < headers.Length; i++)
                        {
                            worksheet.Cells[1, i + 1].Value = headers[i];
                            worksheet.Cells[1, i + 1].Style.Font.Bold = true;
                            worksheet.Cells[1, i + 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
                            worksheet.Cells[1, i + 1].Style.Fill.BackgroundColor.SetColor(Color.LightGray);
                            worksheet.Cells[1, i + 1].Style.Border.BorderAround(ExcelBorderStyle.Thin);
                        }
                    }
                    int nextRow = worksheet.Dimension?.End.Row + 1 ?? 2;  // 找到下一个空行
                    // 写入数据
                    worksheet.Cells[nextRow, 1].Value = result.TestTime.ToString("yyyy-MM-dd HH:mm:ss");
                    worksheet.Cells[nextRow, 2].Value = result.DeviceModel;
                    worksheet.Cells[nextRow, 3].Value = uuid;
                    worksheet.Cells[nextRow, 4].Value = result.ScannedBarcode; // 新增：保存扫描的条码
                    worksheet.Cells[nextRow, 5].Value = result.SerialPort;
                    worksheet.Cells[nextRow, 6].Value = result.FirmwareVersion;
                    worksheet.Cells[nextRow, 7].Value = result.FirmwareResult;
                    worksheet.Cells[nextRow, 8].Value = result.IdleCurrent;
                    worksheet.Cells[nextRow, 9].Value = result.IdleResult;
                    worksheet.Cells[nextRow, 10].Value = result.LoadCurrent;
                    worksheet.Cells[nextRow, 11].Value = result.LoadResult;
                    worksheet.Cells[nextRow, 12].Value = result.Speed;
                    worksheet.Cells[nextRow, 13].Value = result.SpeedResult;
                    worksheet.Cells[nextRow, 14].Value = result.Position;
                    worksheet.Cells[nextRow, 15].Value = result.PositionResult;
                    worksheet.Cells[nextRow, 16].Value = result.Temperature;
                    worksheet.Cells[nextRow, 17].Value = result.TempResult;
                    worksheet.Cells[nextRow, 18].Value = result.Voltage;
                    worksheet.Cells[nextRow, 19].Value = result.VoltageResult;
                    worksheet.Cells[nextRow, 20].Value = result.OpenLoopTest;
                    worksheet.Cells[nextRow, 21].Value = result.OverallResult;
                    for (int i = 1; i <= 21; i++)  // 设置单元格样式
                    {
                        worksheet.Cells[nextRow, i].Style.Border.BorderAround(ExcelBorderStyle.Thin);

                        if (i % 2 == 1 && i >= 7 && i <= 19) // 结果列：7,9,11,13,15,17,21
                        {
                            string value = worksheet.Cells[nextRow, i].Value?.ToString();
                            if (value == "OK")
                            {
                                worksheet.Cells[nextRow, i].Style.Fill.PatternType = ExcelFillStyle.Solid;
                                worksheet.Cells[nextRow, i].Style.Fill.BackgroundColor.SetColor(Color.LightGreen);
                            }
                            else if (value == "NG")
                            {
                                worksheet.Cells[nextRow, i].Style.Fill.PatternType = ExcelFillStyle.Solid;
                                worksheet.Cells[nextRow, i].Style.Fill.BackgroundColor.SetColor(Color.LightCoral);
                            }
                        }                        
                        if (i == 21)  // 总体结果列
                        {
                            string value = worksheet.Cells[nextRow, i].Value?.ToString();
                            if (value == "OK")
                            {
                                worksheet.Cells[nextRow, i].Style.Fill.PatternType = ExcelFillStyle.Solid;
                                worksheet.Cells[nextRow, i].Style.Fill.BackgroundColor.SetColor(Color.LightGreen);
                                worksheet.Cells[nextRow, i].Style.Font.Bold = true;
                            }
                            else if (value == "NG")
                            {
                                worksheet.Cells[nextRow, i].Style.Fill.PatternType = ExcelFillStyle.Solid;
                                worksheet.Cells[nextRow, i].Style.Fill.BackgroundColor.SetColor(Color.LightCoral);
                                worksheet.Cells[nextRow, i].Style.Font.Bold = true;
                            }
                        }
                    }                   
                    worksheet.Cells.AutoFitColumns();  // 自动调整列宽
                    package.SaveAs(file); // 保存文件
                }
                // 保存成功后设置为只读权限
                if (File.Exists(filePath))
                {
                    FileAttributes attributes = File.GetAttributes(filePath);
                    File.SetAttributes(filePath, attributes | FileAttributes.ReadOnly);
                }
            }
            catch (IOException ex) when (ex.Message.Contains("正在被另一个进程使用") ||
                                       ex.Message.Contains("being used by another process"))
            {
                Invoke(() =>
                {
                    MessageBox.Show($"Excel文件被其他进程占用:{ex.Message}",
                        "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                });
            }
            catch (Exception ex)
            {
                Invoke(() =>
                {
                    MessageBox.Show($"保存Excel文件失败：{ex.Message}", "错误",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                });
            }
            finally
            {
                ReleaseLockFile(lockStream, lockFilePath);
            }
        }
        private bool TryCreateLockFile(string lockFilePath, out FileStream lockStream)
        {
            lockStream = null;
            try
            {               
                if (File.Exists(lockFilePath))  // 检查锁文件是否已存在且创建时间超过5分钟（防止死锁）
                {
                    FileInfo lockFileInfo = new FileInfo(lockFilePath);
                    if (DateTime.Now - lockFileInfo.CreationTime > TimeSpan.FromMinutes(0.5))
                    {
                        try
                        {
                            File.Delete(lockFilePath);
                        }
                        catch
                        {
                            return false; // 删除失败，仍然被占用
                        }
                    }
                    else
                    {
                        return false; // 锁文件存在且较新，说明正在使用
                    }
                }

                lockStream = new FileStream(lockFilePath, FileMode.CreateNew,
                                           FileAccess.Write, FileShare.None);
                byte[] info = System.Text.Encoding.UTF8.GetBytes(
                    $"Process: {System.Diagnostics.Process.GetCurrentProcess().Id}\r\n" +
                    $"Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n" +
                    $"Machine: {Environment.MachineName}");
                lockStream.Write(info, 0, info.Length);
                lockStream.Flush();
                return true;
            }
            catch (IOException)
            {
                lockStream?.Dispose();
                lockStream = null;
                return false;
            }
            catch (Exception)
            {
                lockStream?.Dispose();
                lockStream = null;
                return false;
            }
        }

        private void SetExcelFilesReadOnly()
        {
            try
            {
                string currentDirectory = System.Windows.Forms.Application.StartupPath;
                string parentDirectory = Path.Combine(currentDirectory, "..");
                string fullParentDirectory = Path.GetFullPath(parentDirectory);
                string folderPath = Path.Combine(fullParentDirectory, "测试数据备份");

                if (Directory.Exists(folderPath))
                {
                    string[] excelFiles = Directory.GetFiles(folderPath, "*.xlsx");
                    foreach (string filePath in excelFiles)
                    {
                        FileAttributes attributes = File.GetAttributes(filePath);
                        if ((attributes & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
                        {
                            File.SetAttributes(filePath, attributes | FileAttributes.ReadOnly);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"设置文件只读权限失败：{ex.Message}", "警告",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        // 新增：释放文件锁
        private void ReleaseLockFile(FileStream lockStream, string lockFilePath)
        {
            try
            {
                lockStream?.Close();
                lockStream?.Dispose();
            }
            catch { /* 忽略关闭异常 */ }
            try
            {
                if (File.Exists(lockFilePath))
                {
                    File.Delete(lockFilePath);
                }
            }
            catch { /* 忽略删除异常 */ }
        }
        public string GetUid(byte[] response)
        {
            if (response == null || response.Length < 7)
                return null;

            byte[] dataToCheck = new byte[response.Length - 3];
            Array.Copy(response, 2, dataToCheck, 0, dataToCheck.Length);
            byte calculatedChecksum = CalculateChecksum(dataToCheck);
            if (calculatedChecksum != response[response.Length - 1])
                return null;

            int paramStartIndex = 6; // 原代码从第6字节开始
            int paramLength = response.Length - paramStartIndex - 1; // 排除校验和

            byte[] parameters = new byte[paramLength];
            Array.Copy(response, paramStartIndex, parameters, 0, paramLength);
            string mergedValue = "";

            if (parameters.Length > 0)
            {
                int parametersLength = parameters.Length;
                if (parameters.Length > 12) parametersLength = 12;

                for (int i = 0; i < parametersLength; i++)
                    mergedValue += parameters[i].ToString("X2"); // 低位在前合并
            }
            return mergedValue;
        }

        public int GetResponseParameters(byte[] response)
        {
            if (response == null || response.Length < 7)
                return -2147483647;

            byte[] dataToCheck = new byte[response.Length - 3];
            Array.Copy(response, 2, dataToCheck, 0, dataToCheck.Length);
            byte calculatedChecksum = CalculateChecksum(dataToCheck);
            if (calculatedChecksum != response[response.Length - 1])
                return -2147483647;

            int paramStartIndex = 6; // 原代码从第6字节开始
            int paramLength = response.Length - paramStartIndex - 1; // 排除校验和

            byte[] parameters = new byte[paramLength];
            Array.Copy(response, paramStartIndex, parameters, 0, paramLength);
            int mergedValue = -2147483647;

            if (parameters.Length > 0)
            {
                int parametersLength = parameters.Length;
                if (parameters.Length > 4) parametersLength = 4;
                mergedValue = 0;
                for (int i = 0; i < parametersLength; i++)
                    mergedValue |= (parameters[i] << (8 * i)); // 低位在前合并
            }
            return mergedValue;
        }

        public byte[]? SendCommand(byte command, byte address, byte[]? data = null)
        {
            byte[] frameHeader = { 0xF9, 0xFF };
            byte dataLength = (byte)(3 + (data != null ? data.Length : 0));

            byte[] commandPacket = new byte[7 + (data != null ? data.Length : 0)];
            Array.Copy(frameHeader, 0, commandPacket, 0, 2);
            commandPacket[2] = ID;
            commandPacket[3] = dataLength;
            commandPacket[4] = command;
            commandPacket[5] = address;

            if (data != null)
            {
                Array.Copy(data, 0, commandPacket, 6, data.Length);
            }

            byte[] checksumData = new byte[4 + (data != null ? data.Length : 0)];
            Array.Copy(commandPacket, 2, checksumData, 0, checksumData.Length);
            byte checksum = CalculateChecksum(checksumData);
            commandPacket[commandPacket.Length - 1] = checksum;

            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Write(commandPacket, 0, commandPacket.Length);
                    Trace.WriteLine(commandPacket);

                    byte[] responseBuffer = new byte[256];
                    int bytesRead;
                    try
                    {
                        Thread.Sleep(50);
                        bytesRead = serialPort.Read(responseBuffer, 0, responseBuffer.Length);
                    }
                    catch (TimeoutException)
                    {
                        Console.WriteLine("读取数据超时，请检查舵机连接和通信设置。");
                        return null;
                    }
                    byte[] actualResponse = new byte[bytesRead];
                    Array.Copy(responseBuffer, actualResponse, bytesRead);
                    return actualResponse;
                }
                else
                {
                    Console.WriteLine("串口未打开，无法发送命令。");
                    return null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发送命令时出错: {ex.Message}");
                return null;
            }
        }

        private byte CalculateChecksum(params byte[] data)
        {
            byte sum = 0;
            foreach (byte b in data)
            {
                sum += b;
            }
            return (byte)~sum;
        }

        int count = 0;
        private void timer1_Tick(object sender, EventArgs e)
        {
            count++;
            if (count % 2 == 0)
            {
                panel8.BackColor = Color.Yellow;
                panel8.Refresh();
            }
            else
            {
                panel8.BackColor = Color.WhiteSmoke;
                panel8.Refresh();
            }
        }

        private void panel8_DoubleClick(object sender, EventArgs e)
        {
            if (isTesting == true)
            {
                return;
            }

            // 检查是否有有效的条码
            if (string.IsNullOrEmpty(scannedBarcode))
            {
                MessageBox.Show("请先扫描有效的产品条码！", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                textBox1.Focus();
                return;
            }

            isTesting = true;
            cancelTest = new CancellationTokenSource();
            btnStartTest.Text = "停止";
            try
            {
                for (int i = 1; i <= 7; i++)
                {
                    var foundControls1 = this.Controls.Find($"value{i}", true);
                    var foundControls2 = this.Controls.Find($"result{i}", true);
                    var foundControls3 = this.Controls.Find($"panel{i}", true);
                    foundControls1[0].Text = "";
                    foundControls2[0].Text = "";
                    foundControls3[0].BackColor = Color.WhiteSmoke;
                }
                value8.Text = "";
                panel8.BackColor = Color.WhiteSmoke;
                state = TestState.open;
                Task.Run(() => RunTestSequence(cancelTest.Token), cancelTest.Token);
            }
            catch (OperationCanceledException)
            {
                isTesting = false;
                cancelTest?.Cancel();
                btnStartTest.Text = "测试";
            }
        }
        private bool isExamineMode = false; // 添加到类的字段中

        private void examine(object sender, EventArgs e)
        {
            isExamineMode = !isExamineMode;  // 切换检查模式状态
            if (isExamineMode)
            {
                btn_examine.Text = "退出检查模式";
                btn_examine.BackColor = Color.Orange;
                textBox1.Enabled = false;
                textBox1.BackColor = Color.LightGray;
                textBox1.Text = "出货检查模式 - 无需扫码";
                scannedBarcode = "出货检查模式" + DateTime.Now.ToString("yyyyMMdd");  // 设置一个默认的检查模式条码
                if (serialPort.IsOpen)
                {
                    btnStartTest.Enabled = true;
                }
                // 显示提示信息
                //MessageBox.Show("已进入出货检查模式！\n在此模式下无需扫描条码即可进行测试。\n请注意：此模式仅用于出货前检查。",
                //               "检查模式", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                btn_examine.Text = "出货检查模式";
                btn_examine.BackColor = SystemColors.Control;
                textBox1.Enabled = true;
                textBox1.BackColor = Color.White;
                textBox1.Text = "请扫描产品条码...";
                textBox1.ForeColor = Color.Gray;
                scannedBarcode = "";
                btnStartTest.Enabled = false; // 重新设置按钮状态（需要有效条码才能测试）
                textBox1.Focus();

            }
        }

    }
}